#include <gmStringObject.h>
#include <gmThread.h>
#include "Player.h"

///Player
Player::Player(int id, GMClient* client):
    m_id(id), m_client(client){}


///GMPlayer
gmType GMPlayer::m_userType = GM_NULL;
static gmStringObject* GMSTRING_CLIENT = NULL;

std::map<gmptr, PlayerProperty> GMPlayer::m_propertyMap;

GMPlayer::GMPlayer(gmMachine* machine, int id, GMClient* client):
    Player(id, client) {
    m_userObject = machine->AllocUserObject(this, m_userType);
    m_userTable = machine->AllocTableObject();
}

GMPlayer::~GMPlayer()
{
    m_userObject->m_user = NULL;
}

bool GM_CDECL GMPlayer::GMTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
    GMPlayer* thisObject = static_cast<GMPlayer*>(a_object->m_user);

    a_gc->GetNextObject(thisObject->m_userTable);
    ++a_workDone;

    if(thisObject->m_client) {
        a_gc->GetNextObject(thisObject->m_client->GetUserObject());
        ++a_workDone;
    }

    for(size_t i = 0; i < PLAYER_PROP_ENUM_SIZE; ++i)
    {
        gmVariable& prop = thisObject->props[i];

        if(prop.IsReference()) {
            a_gc->GetNextObject((gmObject *)prop.m_value.m_ref);
            ++a_workDone;
        }
    }
    ++a_workDone;
    return true;
}

void GM_CDECL GMPlayer::GMOPDotGet(gmThread * a_thread, gmVariable * a_operands)
{
    GMPlayer* thisObject = static_cast<GMPlayer*>(a_operands[0].GetUserSafe(GMPlayer::GetType()));

    if(thisObject)
    {
        PlayerProperty prop = GMGetPropertyIndex(a_operands[1].m_value.m_ref);

        if(prop != PLAYER_PROP_INVALID)
            a_operands[0] = thisObject->GetProperty(prop);

        else if(a_operands[1].m_value.m_ref == GMSTRING_CLIENT->GetRef() && thisObject->m_client)
            a_operands[0].SetUser(thisObject->m_client->GetUserObject());

        else a_operands[0] = thisObject->m_userTable->Get(a_operands[1]);
    }
}

void GM_CDECL GMPlayer::GMOPDotSet(gmThread * a_thread, gmVariable * a_operands)
{
    GMPlayer* thisObject = static_cast<GMPlayer*>(a_operands[0].GetUserSafe(GMPlayer::GetType()));

    if(thisObject)
    {
        PlayerProperty prop = GMGetPropertyIndex(a_operands[2].m_value.m_ref);

        if(prop != PLAYER_PROP_INVALID)
            thisObject->SetProperty(prop, a_operands[1]);

        else thisObject->m_userTable->Set(a_thread->GetMachine(), a_operands[2], a_operands[1]);
    }
}


PlayerProperty GMPlayer::GMGetPropertyIndex(gmptr propRef)
{
    std::map<gmptr, PlayerProperty>::iterator it = m_propertyMap.find(propRef);

    if(it != m_propertyMap.end())
        return it->second;
    return PLAYER_PROP_INVALID;
}

void GMPlayer::AssignGMPropertyIndex(gmMachine* machine, const std::string& propName, PlayerProperty prop)
{
    m_propertyMap[machine->AllocPermanantStringObject(propName.c_str())->GetRef()] = prop;
}


void GMPlayer::Register(gmMachine* machine)
{
    GMSTRING_CLIENT = machine->AllocPermanantStringObject("client");

    m_userType = machine->CreateUserType("Player");
    AssignGMPropertyIndex(machine, "x", PLAYER_PROP_X);
    AssignGMPropertyIndex(machine, "y", PLAYER_PROP_Y);
    AssignGMPropertyIndex(machine, "rupees", PLAYER_PROP_RUPEES);
    AssignGMPropertyIndex(machine, "bombs", PLAYER_PROP_BOMBS);
    AssignGMPropertyIndex(machine, "darts", PLAYER_PROP_DARTS);
    AssignGMPropertyIndex(machine, "mp", PLAYER_PROP_MP);
    AssignGMPropertyIndex(machine, "ap", PLAYER_PROP_AP);
    AssignGMPropertyIndex(machine, "hearts", PLAYER_PROP_HEARTS);
    AssignGMPropertyIndex(machine, "fullhearts", PLAYER_PROP_FULL_HEARTS);
    AssignGMPropertyIndex(machine, "dir", PLAYER_PROP_DIR);
    AssignGMPropertyIndex(machine, "glovepower", PLAYER_PROP_GLOVE_POWER);
    AssignGMPropertyIndex(machine, "swordpower", PLAYER_PROP_SWORD_POWER);
    AssignGMPropertyIndex(machine, "shieldpower", PLAYER_PROP_SHIELD_POWER);
    AssignGMPropertyIndex(machine, "headimage", PLAYER_PROP_HEAD_IMAGE);
    AssignGMPropertyIndex(machine, "sprite", PLAYER_PROP_SPRITE);
    AssignGMPropertyIndex(machine, "nickname", PLAYER_PROP_NICK);
    AssignGMPropertyIndex(machine, "ani", PLAYER_PROP_ANI);
    AssignGMPropertyIndex(machine, "chat", PLAYER_PROP_CHAT);

    machine->RegisterTypeOperator(m_userType, O_GETDOT, NULL, GMOPDotGet);
    machine->RegisterTypeOperator(m_userType, O_SETDOT, NULL, GMOPDotSet);
    machine->RegisterUserCallbacks(m_userType, GMTrace, NULL, NULL);
}


